package NsingCHM.Files;

/** 
 FtpLib ��ժҪ˵����
*/
public class FtpLib
{
	private String remoteHost,remotePath,remoteUser,remotePass,mes;
	private int remotePort,bytes;
	private Socket clientSocket;

	private int retValue;
	private boolean debug;
	private boolean logined;
	private String reply;

	private static int BLOCK_SIZE = 512;

//C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java:
//ORIGINAL LINE: Byte[] buffer = new Byte[BLOCK_SIZE];
	private byte[] buffer = new byte[BLOCK_SIZE];
	private Encoding ASCII = Encoding.ASCII;

	public FtpLib()
	{
		//
		// TODO: �ڴ˴���ӹ��캯���߼�
		//
		remoteHost = "localhost";
		remotePath = ".";
		remoteUser = "anonymous";
		remotePass = "jaimon@school2000.co.uk";
		remotePort = 21;
		debug = false;
		logined = false;
	}

	/**
	 Set the name of the FTP server to connect to.
	��������FTP�������ĵ�ַ��IP��������
	 Server name
	*/
	public final void setRemoteHost(String remoteHost)
	{
		this.remoteHost = remoteHost;
	}

	/**
	 Return the name of the current FTP server.
	���ص�ǰFTP����������
	 Server name
	*/
	public final String getRemoteHost()
	{
		return remoteHost;
	}

	/**
	 Set the port number to use for FTP.
	����FTP��½�˿�
	 Port number
	*/
	public final void setRemotePort(int remotePort)
	{
		this.remotePort = remotePort;
	}

	/**
	 Return the current port number.
	���ص�ǰ�˿ں���
	 Current port number
	*/
	public final int getRemotePort()
	{
		return remotePort;
	}

	/**
	 Set the remote directory path.
	����Զ��FTP������·��
	 The remote directory path
	*/
	public final void setRemotePath(String remotePath)
	{
		this.remotePath = remotePath;
	}

	/**
	 Return the current remote directory path.
	����Զ��FTP��������ǰ·��
	 The current remote directory path.
	*/
	public final String getRemotePath()
	{
		return remotePath;
	}

	/**
	 Set the user name to use for logging into the remote 	server.
	����Զ��FTP��������½�û�
	 Username
	*/
	public final void setRemoteUser(String remoteUser)
	{
		this.remoteUser = remoteUser;
	}

	/**
	 Set the password to user for logging into the remote 	server.
	����Զ��FTP��½����
	 Password
	*/
	public final void setRemotePass(String remotePass)
	{
		this.remotePass = remotePass;
	}

	/**
	 Return a string array containing the remote directory's file list.
	����Զ��FTP�ļ�Ŀ¼
	
	
	*/
	public final String[] getFileList(String mask)
	{

		if (!logined)
		{
			login();
		}

		Socket cSocket = createDataSocket();

		sendCommand("NLST " + mask);

		if (!(retValue == 150 || retValue == 125))
		{
			throw new IOException(reply.substring(4));
		}

		mes = "";

		while (true)
		{

			int bytes = cSocket.Receive(buffer, buffer.length, 0);
			mes += ASCII.GetString(buffer, 0, bytes);

			if (bytes < buffer.length)
			{
				break;
			}
		}

		char[] seperator = {'\n'};
		String[] mess = mes.split(java.util.regex.Pattern.quote(seperator.toString()), -1);

		cSocket.Close();

		readReply();

		if (retValue != 226)
		{
			throw new IOException(reply.substring(4));
		}
		return mess;

	}

	/**
	 Return the size of a file.
	�����ļ���С
	
	
	*/
	public final long getFileSize(String fileName)
	{

		if (!logined)
		{
			login();
		}

		sendCommand("SIZE " + fileName);
		long size = 0;

		if (retValue == 213)
		{
			size = Long.parseLong(reply.substring(4));
		}
		else
		{
			throw new IOException(reply.substring(4));
		}

		return size;

	}

	/**
	 Login to the remote server.
	��½Զ��FTP������
	*/
	public final void login()
	{

		clientSocket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
		IPEndPoint ep = new IPEndPoint(Dns.Resolve(remoteHost).AddressList[0], remotePort);

		try
		{
			clientSocket.Connect(ep);
		}
		catch (RuntimeException e)
		{
			throw new IOException("Couldn't connect to remote server");
		}

		readReply();
		if (retValue != 220)
		{
			close();
			throw new IOException(reply.substring(4));
		}
		if (debug)
		{
			System.out.println("USER " + remoteUser);
		}

		sendCommand("USER " + remoteUser);

		if (!(retValue == 331 || retValue == 230))
		{
			cleanup();
			throw new IOException(reply.substring(4));
		}

		if (retValue != 230)
		{
			if (debug)
			{
				System.out.println("PASS xxx");
			}

			sendCommand("PASS " + remotePass);
			if (!(retValue == 230 || retValue == 202))
			{
				cleanup();
				throw new IOException(reply.substring(4));
			}
		}

		logined = true;
		System.out.println("Connected to " + remoteHost);

		chdir(remotePath);

	}

	/**
	 If the value of mode is true, set binary mode for 	downloads.
	 Else, set Ascii mode.
	����FTPģʽ
	
	*/
	public final void setBinaryMode(boolean mode)
	{

		if (mode)
		{
			sendCommand("TYPE I");
		}
		else
		{
			sendCommand("TYPE A");
		}
		if (retValue != 200)
		{
			throw new IOException(reply.substring(4));
		}
	}

	/**
	 Download a file to the Assembly's local directory,
	 keeping the same file name.
	�����ļ������أ�����ԭ�ļ���
	
	*/
	public final void download(String remFileName)
	{
		download(remFileName, "", false);
	}

	/**
	 Download a remote file to the Assembly's local 	directory,
	 keeping the same file name, and set the resume flag.
	�����ļ�
	
	
	*/
	public final void download(String remFileName, boolean resume)
	{
		download(remFileName, "", resume);
	}

	/**
	 Download a remote file to a local file name which can 	include
	 a path. The local file name will be created or 	overwritten,
	 but the path must exist.
	
	
	
	*/
	public final void download(String remFileName, String locFileName)
	{
		download(remFileName, locFileName, false);
	}

	/**
	 Download a remote file to a local file name which can include
	 a path, and set the resume flag. The local file name will be
	 created or overwritten, but the path must exist.
	����һ��Զ���ļ������أ�
	
	
	
	*/
	public final void download(String remFileName, String locFileName, boolean resume)
	{
		if (!logined)
		{
			login();
		}

		setBinaryMode(true);

		System.out.println("Downloading file " + remFileName + " from " + remoteHost + "/" + remotePath);

		if (locFileName.equals(""))
		{
			locFileName = remFileName;
		}

		if (!(new java.io.File(locFileName)).isFile())
		{
			Stream st = File.Create(locFileName);
			st.Close();
		}

		FileStream output = new FileStream(locFileName,FileMode.Open);

		Socket cSocket = createDataSocket();

		long offset = 0;

		if (resume)
		{

			offset = output.getLength();

			if (offset > 0)
			{
				sendCommand("REST " + offset);
				if (retValue != 350)
				{
					//throw new IOException(reply.Substring(4));
					//Some servers may not support resuming.
					offset = 0;
				}
			}

			if (offset > 0)
			{
				if (debug)
				{
					System.out.println("seeking to " + offset);
				}
				long npos = output.Seek(offset,SeekOrigin.Begin);
				System.out.println("new pos=" + npos);
			}
		}

		sendCommand("RETR " + remFileName);

		if (!(retValue == 150 || retValue == 125))
		{
			throw new IOException(reply.substring(4));
		}

		while (true)
		{

			bytes = cSocket.Receive(buffer, buffer.length, 0);
			output.Write(buffer,0,bytes);

			if (bytes <= 0)
			{
				break;
			}
		}

		output.Close();
		if (cSocket.Connected)
		{
			cSocket.Close();
		}

		System.out.println("");

		readReply();

		if (!(retValue == 226 || retValue == 250))
		{
			throw new IOException(reply.substring(4));
		}

	}

	/**
	 Upload a file.
	�ϴ��ļ�
	
	*/
	public final void upload(String fileName)
	{
		upload(fileName, false);
	}

	/**
	 Upload a file and set the resume flag.
	�ϴ��ļ�������������
	
	
	*/
	public final void upload(String fileName, boolean resume)
	{

		if (!logined)
		{
			login();
		}

		Socket cSocket = createDataSocket();
		long offset = 0;

		if (resume)
		{

			try
			{

				setBinaryMode(true);
				offset = getFileSize(fileName);

			}
			catch (RuntimeException e)
			{
				offset = 0;
			}
		}

		if (offset > 0)
		{
			sendCommand("REST " + offset);
			if (retValue != 350)
			{
				//throw new IOException(reply.Substring(4));
				//Remote server may not support resuming.
				offset = 0;
			}
		}

		sendCommand("STOR " + (new java.io.File(fileName)).getName());

		if (!(retValue == 125 || retValue == 150))
		{
			throw new IOException(reply.substring(4));
		}

		// open input stream to read source file
		FileStream input = new FileStream(fileName,FileMode.Open);

		if (offset != 0)
		{

			if (debug)
			{
				System.out.println("seeking to " + offset);
			}
			input.Seek(offset,SeekOrigin.Begin);
		}

		System.out.println("Uploading file " + fileName + " to " + remotePath);

		while ((bytes = input.Read(buffer,0,buffer.length)) > 0)
		{

			cSocket.Send(buffer, bytes, 0);

		}
		input.Close();

		System.out.println("");

		if (cSocket.Connected)
		{
			cSocket.Close();
		}

		readReply();
		if (!(retValue == 226 || retValue == 250))
		{
			throw new IOException(reply.substring(4));
		}
	}


	/**
	 Delete a file from the remote FTP server.
	ɾ��Զ��FTP�������ļ�
	
	*/
	public final void deleteRemoteFile(String fileName)
	{

		if (!logined)
		{
			login();
		}

		sendCommand("DELE " + fileName);

		if (retValue != 250)
		{
			throw new IOException(reply.substring(4));
		}

	}

	/**
	 Rename a file on the remote FTP server.
	�޸�Զ��FTP�������ļ���
	
	
	*/
	public final void renameRemoteFile(String oldFileName, String newFileName)
	{

		if (!logined)
		{
			login();
		}

		sendCommand("RNFR " + oldFileName);

		if (retValue != 350)
		{
			throw new IOException(reply.substring(4));
		}

		//  known problem
		//  rnto will not take care of existing file.
		//  i.e. It will overwrite if newFileName exist
		sendCommand("RNTO " + newFileName);
		if (retValue != 250)
		{
			throw new IOException(reply.substring(4));
		}

	}

	/**
	 Create a directory on the remote FTP server.
	��Զ��FTP�������ϴ����ļ���
	
	*/
	public final void mkdir(String dirName)
	{

		if (!logined)
		{
			login();
		}

		sendCommand("MKD " + dirName);

		if (retValue != 250)
		{
			throw new IOException(reply.substring(4));
		}

	}

	/**
	 Delete a directory on the remote FTP server.
	ɾ��Զ��FTP�������ļ���
	
	*/
	public final void rmdir(String dirName)
	{

		if (!logined)
		{
			login();
		}

		sendCommand("RMD " + dirName);

		if (retValue != 250)
		{
			throw new IOException(reply.substring(4));
		}

	}

	/**
	 Change the current working directory on the remote FTP 	server.
	�ı�Զ�̷�������ǰ����Ŀ¼
	
	*/
	public final void chdir(String dirName)
	{

		if (dirName.equals("."))
		{
			return;
		}

		if (!logined)
		{
			login();
		}

		sendCommand("CWD " + dirName);

		if (retValue != 250)
		{
			throw new IOException(reply.substring(4));
		}

		this.remotePath = dirName;

		System.out.println("Current directory is " + remotePath);

	}


	/**
	 Close the FTP connection.
	�رշ���������
	*/
	public final void close()
	{

		if (clientSocket != null)
		{
			sendCommand("QUIT");
		}

		cleanup();
		System.out.println("Closing...");
	}


	/**
	 Set debug mode.
	
	
	*/
	public final void setDebug(boolean debug)
	{
		this.debug = debug;
	}

	private void readReply()
	{
		mes = "";
		reply = readLine();
		retValue = Integer.parseInt(reply.substring(0,3));
	}

	private void cleanup()
	{
		if (clientSocket != null)
		{
			clientSocket.Close();
			clientSocket = null;
		}
		logined = false;
	}

	private String readLine()
	{

		while (true)
		{
			bytes = clientSocket.Receive(buffer, buffer.length, 0);
			mes += ASCII.GetString(buffer, 0, bytes);
			if (bytes < buffer.length)
			{
				break;
			}
		}

		char[] seperator = {'\n'};
		String[] mess = mes.split(java.util.regex.Pattern.quote(seperator.toString()), -1);

		if (mes.length() > 2)
		{
			mes = mess[mess.length - 2];
		}
		else
		{
			mes = mess[0];
		}

		if (!mes.substring(3, 4).equals(" "))
		{
			return readLine();
		}

		if (debug)
		{
			for (int k = 0;k < mess.length - 1;k++)
			{
				System.out.println(mess[k]);
			}
		}
		return mes;
	}

	private void sendCommand(String command)
	{

//C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java:
//ORIGINAL LINE: Byte[] cmdBytes = Encoding.ASCII.GetBytes((command+"\r\n").ToCharArray());
		byte[] cmdBytes = Encoding.ASCII.GetBytes((command + "\r\n").toCharArray());
		clientSocket.Send(cmdBytes, cmdBytes.length, 0);
		readReply();
	}

	private Socket createDataSocket()
	{

		sendCommand("PASV");

		if (retValue != 227)
		{
			throw new IOException(reply.substring(4));
		}

		int index1 = reply.indexOf('(');
		int index2 = reply.indexOf(')');
		String ipData = reply.substring(index1 + 1, index1 + 1 + index2 - index1 - 1);
		int[] parts = new int[6];

		int len = ipData.length();
		int partCount = 0;
		String buf = "";

		for (int i = 0; i < len && partCount <= 6; i++)
		{

			char ch = Character.Parse(ipData.substring(i, i + 1));
			if (Character.isDigit(ch))
			{
				buf += ch;
			}
			else if (ch != ',')
			{
				throw new IOException("Malformed PASV reply: " + reply);
			}

			if (ch == ',' || i + 1 == len)
			{

				try
				{
					parts[partCount++] = Integer.parseInt(buf);
					buf = "";
				}
				catch (RuntimeException e)
				{
					throw new IOException("Malformed PASV reply: " + reply);
				}
			}
		}

		String ipAddress = parts[0] + "." + parts[1] + "." + parts[2] + "." + parts[3];

		int port = (parts[4] << 8) + parts[5];

		Socket s = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
		IPEndPoint ep = new IPEndPoint(Dns.Resolve(ipAddress).AddressList[0], port);

		try
		{
			s.Connect(ep);
		}
		catch (RuntimeException e2)
		{
			throw new IOException("Can't connect to remote 	server");
		}

		return s;
	}
}